package cn.spy.threaddownload.utils;

import cn.spy.threaddownload.exception.CustomException;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class FileOptUtils {

    /**
     * 判断是否为文件
     * @param path
     * @return
     */
    public static boolean isFile(String path) {
        File file = new File(path);

        if (!file.exists()) {
            throw new RuntimeException("文件不存在！");
        }

        return file.isFile();
    }

    /**
     * 判断是否为文件
     * @param file
     * @return
     */
    public static boolean isFile(File file) {
        if (!file.exists()) {
            throw new RuntimeException("文件不存在！");
        }

        return file.isFile();
    }


    /**
     * 判断文件是否存在
     * @param path
     * @return
     */
    public static boolean isExist(String path) {
        File file = new File(path);
        return file.exists();
    }

    /**
     * 判断是否为文件夹
     * @param path
     * @return
     */
    public static boolean isDir(String path) {
        File file = new File(path);

        if (!file.exists()) {
            throw new RuntimeException("目录不存在！");
        }

        return file.isDirectory();
    }

    /**
     * 判断是否为文件夹
     *
     * @param file
     * @return
     */
    public static boolean isDir(File file) {
        if (!file.exists()) {
            throw new RuntimeException("目录不存在！");
        }

        return file.isDirectory();
    }


    /**
     * 创建目录
     * @param dirPath
     * @return
     */
    public static File mkdirs(String dirPath) {
        if (dirPath == null) {
            return null;
        } else {
            File dir = new File(dirPath);
            return mkdirs(dir);
        }
    }

    /**
     * 创建目录（不存在的文件依次创建）
     * @param dir
     * @return
     */
    public static File mkdirs(File dir) {
        if (dir == null) {
            return null;
        } else {
            if (!dir.exists()) {
                dir.mkdirs();
            }

            return dir;
        }
    }

    /**
     * 获取文件夹下面的所有文件
     * @param dirPath
     * @return
     */
    public static List<File> listFile(String dirPath) {
        File dirFile = new File(dirPath);

        if (!dirFile.exists()) {
            throw new RuntimeException("目录不存在！");
        }

        File[] arr = dirFile.listFiles();

        if (arr == null || arr.length == 0) {
            return null;
        }

        return Arrays.asList(arr);
    }

    /**
     * 输出流写文件
     * @param fos
     * @param data
     */
    public static void writeFileByNIO(FileOutputStream fos, byte[] data) {
        if (Objects.isNull(fos)) {
            throw new NullPointerException();
        }

        try {
            FileChannel fc = fos.getChannel();
            // 创建缓冲区
            ByteBuffer buf = ByteBuffer.allocate(data.length);
            // 将数据装入缓冲区
            buf.put(data);
            // 反转缓冲区(limit设置为position,position设置为0,mark设置为-1)
            buf.flip();
            // 将缓冲区中的数据写入到通道
            fc.write(buf);
            // 写完将缓冲区还原(position设置为0,limit设置为capacity,mark设置为-1)
            buf.clear();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static Boolean transfer(InputStream is, File destFile, int start, int end) {
        int capacity = end - start;
        byte[] arr = new byte[capacity];
        try {
            is.skip(start);

            while (is.read(arr) != -1 && is.read(arr) != capacity) {
                writeFileByNIO(destFile, arr);
            }

            return true;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 写数据到目标文件
     * @param destFile
     * @param data
     * @return
     */
    public static Boolean writeFileByNIO(File destFile, byte[] data) {
        try {
            FileOutputStream fos = new FileOutputStream(destFile, true);
            writeFileByNIO(fos, data);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }

        return true;
    }

    /**
     * 写数据到目标地址
     *
     * @param destFilePath
     * @param data
     */
    public static void writeFileByNIO(String destFilePath, byte[] data) {
        if (StringUtils.isEmpty(destFilePath)) {
            throw new NullPointerException();
        }

        File destFile = new File(destFilePath);
        writeFileByNIO(destFile, data);
    }

    /**
     * 写数据到指定的目标目录+目标文件名
     *
     * @param destDir
     * @param destFileName
     * @param data
     */
    public static void writeFileByNIO(String destDir, String destFileName, byte[] data) {
        if (StringUtils.isEmpty(destDir) || StringUtils.isEmpty(destFileName)) {
            throw new NullPointerException();
        }

        File destFile = new File(destDir + File.separator + destFileName);
        writeFileByNIO(destFile, data);
    }

    /**
     * 从指定的File读文件(RandomAccessFile)
     * @param srcFile 源文件
     * @param start 开始读文件的位置
     * @param end 结束读文件的位置
     * @return
     */
    public static byte[] readFile(File srcFile, int start, int end) {
        if (srcFile == null) {
            throw new RuntimeException("文件不能为空！");
        }

        if (end < start) {
            throw new RuntimeException("文件数据结束位置不能小于开始位置");
        }

        int bufferSize = end - start;
        RandomAccessFile randomAccessFile = null;
        byte[] dataArr = new byte[bufferSize];

        try {
            randomAccessFile = new RandomAccessFile(srcFile, "rw");
            randomAccessFile.seek(start);
            randomAccessFile.readFully(dataArr, 0, bufferSize);
            return dataArr;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (randomAccessFile != null) {
                    randomAccessFile.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 直接通过指定的流去读文件(文件流)
     * @param fis 源文件流
     * @param start 开始读文件的位置
     * @param end 结束读文件的位置
     * @return
     */
    public static byte[] readFile(FileInputStream fis, int start, int end) {
        if (Objects.isNull(fis)) {
            throw new NullPointerException();
        }

        if (end < start) {
            throw new RuntimeException("文件数据结束位置不能小于开始位置");
        }

        int bufferSize = end - start;
        byte[] dataArr = new byte[bufferSize];

        try {
            fis.skip(start);
            fis.read(dataArr, 0, bufferSize);
            return dataArr;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 从指定的File读文件(文件流)
     * @param srcFile
     * @param start
     * @param end
     * @return
     */
    public static byte[] readFile2(File srcFile, int start, int end) {
        if (srcFile == null) {
            throw new RuntimeException("文件不能为空！");
        }

        if (end < start) {
            throw new RuntimeException("文件数据结束位置不能小于开始位置");
        }

        try {
            FileInputStream fis = new FileInputStream(srcFile);
            byte[] data = readFile(fis, start, end);
            return data;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static byte[] readFile4(InputStream is, int start, int end) {
        int capacity = end - start;
        byte[] arr = new byte[capacity];

        try {
            is.skip(start);

            while (true) {
                if (!(is.read(arr) != -1 && is.read(arr) != capacity)) break;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return arr;
    }


    public static byte[] readFile3(BufferedInputStream bis, int start, int end) {
        int capacity = end - start;
        byte[] arr = new byte[capacity];
        try {
            while (bis.read(arr, start, capacity) != -1) {

            }

            return arr;
        } catch (IOException e) {
            throw new CustomException(e.getMessage());
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 移动文件
     * @param srcFilePath
     * @param destFilePath
     */
    public static void moveFile(String srcFilePath, String destFilePath) {
        if (StringUtils.isEmpty(srcFilePath) || StringUtils.isEmpty(destFilePath)) {
            throw new NullPointerException();
        }

        File srcFile = new File(srcFilePath);
        File destFile = new File(destFilePath);

        byte[] arr = readFile2(srcFile, 0, (int) srcFile.length());
        writeFileByNIO(destFile, arr);
    }

    /**
     * 清空文件夹
     *
     * @param file
     */
    public static void deleteFile(File file) {
        File[] sonFileArr = file.listFiles();

        for (File sonFile : sonFileArr) {
            if (isFile(sonFile)) {
                sonFile.delete();
            }

            if (isDir(sonFile)) {
                String[] arr = sonFile.list();

                if (arr != null && arr.length > 0) {
                    deleteFile(sonFile);
                } else {
                    sonFile.delete();
                }
            }
        }
    }

    /**
     * 清空文件夹
     *
     * @param dirPath
     */
    public static void clearDir(String dirPath) {
        File file = new File(dirPath);
        if (!isDir(dirPath)) {
            throw new RuntimeException("该文件夹不存在！");
        }

        deleteFile(file);
    }



}
